home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / AdobeExamples / NX_ImportInt / DrawingView.m < prev    next >
Encoding:
Text File  |  1992-12-19  |  24.8 KB  |  1,090 lines

  1.  
  2. /*
  3.  * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
  4.  *
  5.  * (b)  If this Sample Code is distributed as part of the Display PostScript
  6.  *    System Software Development Kit from Adobe Systems Incorporated,
  7.  *    then this copy is designated as Development Software and its use is
  8.  *    subject to the terms of the License Agreement attached to such Kit.
  9.  *
  10.  * (c)  If this Sample Code is distributed independently, then the following
  11.  *    terms apply:
  12.  *
  13.  * (d)  This file may be freely copied and redistributed as long as:
  14.  *    1) Parts (a), (d), (e) and (f) continue to be included in the file,
  15.  *    2) If the file has been modified in any way, a notice of such
  16.  *      modification is conspicuously indicated.
  17.  *
  18.  * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
  19.  *    Adobe Systems Incorporated.
  20.  * 
  21.  * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
  22.  *    CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
  23.  *    AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
  24.  *    ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
  25.  *    OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
  26.  *    WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
  27.  *    WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
  28.  *    DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
  29.  *    FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
  30.  *    OF THIRD PARTY RIGHTS.
  31.  */
  32.  
  33. /*
  34.  *    DrawingView.m
  35.  *
  36.  *    This view represents the page that the image is drawn onto.
  37.  *
  38.  *    An offscreen buffer, bufferId, is used to draw into and then
  39.  *    this buffer is composited onscreen. This technique allows for the
  40.  *    separation of the static drawing from the temporal drawing (modal
  41.  *    loop redrawing and control point display).  The static drawing takes
  42.  *    place in the buffer while the temporal drawing takes place in this view.
  43.  *    The static drawing is stuff that is complex and that will stay around for
  44.  *    a while. Saving this drawing in a buffer elimates having to redraw it
  45.  *    for something simple like drawing a control point or something.
  46.  *    (The bufferId is the content view of a plain window the size of this view);
  47.  *
  48.  *    Version:    2.0
  49.  *    Author:    Ken Fromm
  50.  *    History:
  51.  *            03-17-91        Added this comment, fixed the preview exporting
  52.  *                        section.
  53.  */
  54.  
  55. #import "DrawingView.h"
  56. #import "Document.h"
  57. #import "GraphicImport.h"
  58. #import "SaveAsPanel.h"
  59. #import "epsf.h"
  60.  
  61. #import "DrawingViewWraps.h"
  62.  
  63. #import <appkit/Cell.h>
  64. #import <appkit/ClipView.h>
  65. #import <appkit/Matrix.h>
  66. #import <appkit/NXCursor.h>
  67. #import <appkit/NXImage.h>
  68. #import <appkit/NXBitmapImageRep.h>
  69. #import <appkit/Text.h>
  70. #import <appkit/nextstd.h>
  71.  
  72. #import <dpsclient/dpsclient.h>
  73. #import <dpsclient/wraps.h>
  74. #import <math.h>
  75.  
  76. extern char    ControlFont[ ];
  77.  
  78. extern const NXRect  DefaultWindowRect;
  79.  
  80. static char    EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0";
  81.  
  82. void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op)
  83. {
  84.       PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce),
  85.                 gstate, dest->x, dest->y, op);
  86. }
  87.  
  88. /*
  89. *    The buffer is used to prevent unnecessary drawing by
  90. *    retaining the image offscreen.
  91. *
  92. *    Create a plain window the size of the rectangle passed in and
  93. *    then insert a view into the window as a subview. 
  94. */
  95. static id createBuffer(const NXSize *size)
  96. {
  97.     id        buffer, window;
  98.  
  99.     NXRect    contRect;
  100.     
  101.     contRect.origin.x = contRect.origin.y = 0;
  102.     contRect.size = *size;
  103.     window = [Window newContent:&contRect
  104.                 style:NX_PLAINSTYLE
  105.                 backing:NX_RETAINED
  106.                 buttonMask:0
  107.                 defer:NO] ;
  108.  
  109.     buffer = [[[View  newFrame:&contRect] allocateGState] setClipping:NO];
  110.     [[window  setContentView:buffer ] free];
  111.  
  112.     [ window  display];
  113.  
  114.     return buffer;
  115. }
  116.  
  117. @implementation DrawingView
  118.  
  119. +newFrame:(const NXRect *) frameRect
  120. {    
  121.     self = [super  newFrame:frameRect]; 
  122.     [[self  allocateGState] setClipping:NO]; 
  123.     
  124.     bufferId = createBuffer(&frameRect->size);
  125.  
  126.     return self;
  127. }
  128.  
  129. - free
  130. {
  131.     [graphicId  free];
  132.     
  133.     return [super  free];
  134. }
  135.  
  136. - buffer
  137. {
  138.     return bufferId;
  139. }
  140.  
  141. - image
  142. {
  143.     return imageId;
  144. }
  145.  
  146. - (float) controlPointSize
  147. {
  148.     return  FONTSIZE;
  149. }
  150.  
  151. - resetCursorRects
  152. {
  153.     [self addCursorRect:&bounds  cursor:[NXApp  cursor]];
  154.  
  155.     return self;
  156. }
  157.  
  158. /*
  159. *    This method copies the PostScript code for the graphics and writes it to the
  160. *    stream passed in. Includes the preview image when appropriate.
  161. */
  162. - writePSToStream:(NXStream *) stream
  163. {
  164.     id            nximageId;
  165.  
  166.     NXRect        bbox;
  167.  
  168.     if (stream)
  169.     {
  170.         nximageId = NULL;
  171.         imageId = NULL;
  172.  
  173.         [graphicId  getBounds:&bbox];
  174.         if ([[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
  175.         {
  176.             nximageId = [[NXImage  alloc]  initSize:&bbox.size];
  177.             [nximageId  useCacheWithDepth:NX_TwoBitGrayDepth];
  178.             if ([nximageId  lockFocus])
  179.             {
  180.                 PStranslate(-bbox.origin.x, -bbox.origin.y);
  181.                 PSsetgray(NX_WHITE);
  182.                 NXRectFill(&bbox);
  183.                 [graphicId  drawObject:&bbox  withFlags:NOFLAGS  inView:self];
  184.  
  185.                 imageId = [[NXBitmapImageRep  alloc]  initData:NULL  fromRect:&bbox];
  186.                 [nximageId  unlockFocus];
  187.             }
  188.         }
  189.  
  190.         [self copyPSCodeInside:&bbox to:stream];
  191.  
  192.         [nximageId  free];
  193.         [imageId  free];
  194.         imageId = NULL;
  195.     }
  196.  
  197.     return self;
  198. }
  199.  
  200. /*  Deletes the epsf object if it is selected.  */
  201. - delete:sender
  202. {
  203.     float            knobsize;
  204.  
  205.     NXRect        rect;
  206.  
  207.     if ([graphicId  selected])
  208.     {
  209.         knobsize = -[self  controlPointSize];
  210.         [graphicId  getBounds:&rect];
  211.         NXInsetRect(&rect, knobsize, knobsize);
  212.         [graphicId  free];
  213.         graphicId = NULL;
  214.  
  215.         [self  display:&rect  :1];
  216.     }
  217.  
  218.     return self;
  219. }
  220.  
  221. /*
  222. *    Constrain the point within the view. An offset is needed because when
  223. *    an object is moved, it is often grabbed in the center of the object. If the
  224. *    lower left offset and the upper right offset were not included then part of
  225. *    the object could be moved off of the view. (In some applications, that might
  226. *    be allowed but in this one the object is constrained to always lie in the
  227. *    page.)
  228. */
  229. - constrainPoint:(NXPoint *)aPt  withOffset:(const NXSize*)llOffset  :(const NXSize*)urOffset
  230. {
  231.     float            margin;
  232.  
  233.     NXPoint        viewMin, viewMax;
  234.  
  235.     margin = ceil(FONTSIZE/2);
  236.  
  237.     viewMin.x = bounds.origin.x + llOffset->width + margin;
  238.     viewMin.y = bounds.origin.y + llOffset->height + margin;
  239.  
  240.     viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin;
  241.     viewMax.y = bounds.origin.y + bounds.size.height  - urOffset->height - margin;
  242.  
  243.     aPt->x = MAX(viewMin.x, aPt->x);
  244.     aPt->y = MAX(viewMin.y, aPt->y);
  245.  
  246.     aPt->x = MIN(viewMax.x, aPt->x);    
  247.     aPt->y = MIN(viewMax.y, aPt->y);
  248.  
  249.     return self;
  250. }
  251.  
  252. /*
  253. *    Constrain a rectangle within the view.
  254. */
  255. - constrainRect:(NXRect *)aRect
  256. {
  257.     float            margin;
  258.     
  259.     NXPoint        viewMin, viewMax;
  260.  
  261.     margin = ceil(FONTSIZE/2);
  262.  
  263.     viewMin.x = bounds.origin.x + margin;
  264.     viewMin.y = bounds.origin.y + margin;
  265.  
  266.     viewMax.x = bounds.origin.x + bounds.size.width  - aRect->size.width - margin;
  267.     viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin;
  268.  
  269.     aRect->origin.x = MAX(viewMin.x, aRect->origin.x);
  270.     aRect->origin.y = MAX(viewMin.y, aRect->origin.y);
  271.  
  272.     aRect->origin.x = MIN(viewMax.x, aRect->origin.x );    
  273.     aRect->origin.y = MIN(viewMax.y, aRect->origin.y);
  274.  
  275.     return self;
  276. }
  277.  
  278. /*
  279.  *    Redraws the graphic. The image from the buffer is composited
  280.  *    into the window and then the changed object is drawn atop the
  281.  *    old image. A copy of the image is necessary because if the
  282.  *    window were to allow scrolling the buffer would also have to be
  283.  *    scrolled. Were this to happen the old image might have to be redrawn.
  284.  *    As a result, a copy is created and the changes performed on the
  285.  *    copy.  Care is taken to limit the amount of area that must be
  286.  *    composited and redrawn.
  287.  *
  288.  *    Even though scrolling is not allowed in this instance, a copy is
  289.  *    created anyways.
  290.   */
  291. - redrawObject:(int) pt_num
  292. {
  293.     id            copyId;
  294.  
  295.     BOOL        tracking,
  296.                 dirtyFlag = NO;
  297.  
  298.     int            old_mask;
  299.     
  300.     float            knobsize;
  301.  
  302.     NXPoint        pt, pt_last, pt_old, delta;
  303.     
  304.     NXRect        rect_now, rect_start, rect_last;
  305.  
  306.     NXEvent        *event;
  307.  
  308.     /*
  309.     *    The freeTemp method is messaged at the bottom so as not to free
  310.     *    the shared items. 
  311.     */        
  312.     copyId = [graphicId  copy];
  313.  
  314.     knobsize = -[self  controlPointSize];
  315.     [copyId  getBounds:&rect_start];
  316.     NXInsetRect(&rect_start, knobsize, knobsize);
  317.  
  318.     rect_now = rect_last = rect_start;
  319.  
  320.     [copyId  getPoint:pt_num :&pt_last];
  321.     pt_old = pt_last;
  322.  
  323.     old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  324.     event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  325.     if (event->type != NX_MOUSEUP)
  326.     {
  327.         tracking = YES;
  328.         while (tracking)
  329.         {
  330.             pt = pt_old = event->location;
  331.  
  332.             [self convertPoint:&pt  fromView:nil];
  333.             [copyId constrainPoint:&pt  forPtNum:&pt_num
  334.                     inRect:&bounds  withFlags:event->flags];
  335.  
  336.             delta.x = pt.x - pt_last.x;
  337.             delta.y = pt.y - pt_last.y;
  338.  
  339.             if (delta.x || delta.y)
  340.             {
  341.                 dirtyFlag = YES;
  342.                 /* Change the point location and get the new bounds. */
  343.                 [copyId setPoint:pt_num :&delta];
  344.                 [copyId  getBounds:&rect_now];
  345.                 NXInsetRect(&rect_now, knobsize, knobsize);
  346.  
  347.                 /* Composite the old image and then redraw the new one. */
  348.                 compositeBuffer([bufferId  gState], &rect_last, &rect_last.origin, NX_COPY);
  349.                 [self  drawObject:copyId  forRect:&rect_now  withFlags:REDRAWFLAG];
  350.                 [self  drawControl:copyId  forRect:&rect_now  withFlags:NOFLAGS];
  351.  
  352.                 /* Sync up the drawing so it proceeds a little smoother. */
  353.                 [window flushWindow];
  354.                 NXPing();
  355.                 
  356.                 rect_last = rect_now;
  357.                 pt_last = pt;
  358.             }
  359.  
  360.             event = [NXApp getNextEvent:NX_MOUSEUPMASK|
  361.                             NX_MOUSEDRAGGEDMASK];
  362.             tracking = (event->type != NX_MOUSEUP);
  363.         }
  364.     }
  365.     [window setEventMask:old_mask];
  366.  
  367.     [graphicId  freeTemp];
  368.     graphicId = copyId;
  369.  
  370.     if (dirtyFlag)
  371.     {
  372.         /*
  373.         *  The view has already been focused and we know what
  374.         *  has to be redrawn so call drawSelf:: instead of display
  375.         */
  376.         NXUnionRect(&rect_last, &rect_start);
  377.         [self  drawSelf:&rect_start :1];
  378.         [window flushWindow];
  379.         NXPing();
  380.     }
  381.  
  382.     return self;
  383. }
  384.  
  385. /*
  386.  *    Moves the selected objects by performing a translate before drawing
  387.  *    the objects. This approach is used because the objects are drawn
  388.  *    into windows and drawing simply means compositing the windows.
  389.  *
  390.  *    The offsets constrain the selected object to stay within the dimensions
  391.  *    of the view. 
  392.  */
  393. - moveObject:(NXEvent *)event
  394. {
  395.     BOOL        tracking,
  396.                 dirtyFlag = NO;
  397.     
  398.     int            old_mask;
  399.  
  400.     float            knobsize;
  401.  
  402.     NXSize        llOffset, urOffset;
  403.  
  404.     NXPoint        pt, pt_last, pt_old, delta;
  405.  
  406.     NXRect        rect_now, rect_start, rect_last;
  407.  
  408.     knobsize = -[self  controlPointSize];
  409.     [graphicId  getBounds:&rect_start];
  410.     NXInsetRect(&rect_start, knobsize, knobsize);
  411.     rect_now = rect_last = rect_start;
  412.  
  413.     pt_last = pt_old = event->location;
  414.     [self  convertPoint:&pt_last  fromView:nil];
  415.     
  416.     /* Calculate where the mouse point falls relative to the object. */
  417.     llOffset.width = pt_last.x - rect_start.origin.x;
  418.     llOffset.height = pt_last.y - rect_start.origin.y;    
  419.     urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x;
  420.     urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y;
  421.  
  422.     /* Return nil if the the mouse was not dragged. */
  423.     old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  424.     event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  425.     if (event->type != NX_MOUSEUP)
  426.     {
  427.         tracking = YES;
  428.         while (tracking)
  429.         {            
  430.             pt = pt_old = event->location;
  431.                 
  432.             [self  convertPoint:&pt fromView:nil];
  433.             [self  constrainPoint:&pt  withOffset:&llOffset :&urOffset];
  434.             [self  constrainPoint:&pt_last  withOffset:&llOffset :&urOffset];
  435.             delta.x = pt.x - pt_last.x;
  436.             delta.y = pt.y - pt_last.y;
  437.  
  438.             if (delta.x || delta.y)
  439.             {
  440.                 dirtyFlag = YES;
  441.                 NXOffsetRect(&rect_now, delta.x, delta.y);
  442.                 [self  constrainRect:&rect_now];
  443.  
  444.                 /*
  445.                 *  Composite the old image into the window and then
  446.                 *  translate the user space and draw the graphicId object.
  447.                 */
  448.                 compositeBuffer([bufferId  gState], &rect_last, &rect_last.origin, NX_COPY);
  449.                 PSgsave();
  450.                     PStranslate(rect_now.origin.x - rect_start.origin.x,
  451.                         rect_now.origin.y - rect_start.origin.y);
  452.                     [self  drawObject:graphicId  forRect:&rect_start  withFlags:MOVEFLAG];
  453.                     [self  drawControl:graphicId  forRect:&rect_start 
  454.                             withFlags:NOFLAGS];
  455.                 PSgrestore();
  456.  
  457.                 [window flushWindow];
  458.                 NXPing();
  459.                 
  460.                 rect_last = rect_now;
  461.                 pt_last = pt;
  462.             }
  463.             event = [NXApp getNextEvent:NX_MOUSEUPMASK|
  464.                         NX_MOUSEDRAGGEDMASK];
  465.  
  466.             tracking = (event->type != NX_MOUSEUP);
  467.         }
  468.  
  469.         if (dirtyFlag)
  470.         {
  471.             delta.x = rect_now.origin.x - rect_start.origin.x;
  472.             delta.y = rect_now.origin.y - rect_start.origin.y;            [graphicId  moveAll:&delta];
  473.     
  474.             /*
  475.             *  The view has already been focused and we know what
  476.             *  has to be redrawn so call drawSelf:: instead of display
  477.             */
  478.             NXUnionRect(&rect_now, &rect_start);
  479.             [self drawSelf:&rect_start :1];
  480.             [window flushWindow];
  481.             NXPing();
  482.         }
  483.     }
  484.     [window setEventMask:old_mask];
  485.  
  486.     return self;
  487. }
  488.  
  489. /* Check to see whether a control point has been hit. */
  490.  - checkControl:(const NXPoint *) p :(int *) pt_num
  491.  {
  492.     NXRect    hitRect;
  493.  
  494.     NXSetRect(&hitRect, p->x - HITSETTING/2, p->y - HITSETTING/2, HITSETTING, HITSETTING);
  495.  
  496.     return [graphicId  hitControl:&hitRect :pt_num  forSize:[self  controlPointSize]];
  497.  }
  498.  
  499. /* 
  500. *    Check to see whether the epsf object has been hit. Return it if yes.
  501. */
  502. - checkObject:(const NXPoint *) p
  503.  {
  504.     UPath        *hitPoint;
  505.  
  506.     /*  Bounding Box */
  507.     hitPoint = [NXApp  hitPoint];
  508.     hitPoint->pts[0] = floor(p->x - HITSETTING/2);
  509.     hitPoint->pts[1] = floor(p->y - HITSETTING/2);
  510.     hitPoint->pts[2] = ceil(p->x + HITSETTING/2);
  511.     hitPoint->pts[3] = ceil(p->y + HITSETTING/2);
  512.     
  513.     /*  Moveto */
  514.     hitPoint->pts[4] = p->x - HITSETTING/2;
  515.     hitPoint->pts[5] = p->y - HITSETTING/2;
  516.  
  517.     /* Rlineto's */
  518.     hitPoint->pts[7] = HITSETTING;
  519.     hitPoint->pts[8] = HITSETTING;
  520.     hitPoint->pts[11] = -HITSETTING;
  521.  
  522.     return [graphicId  hitObject:hitPoint];
  523.  }
  524.  
  525.  /*
  526.  *    Set the object as selected then draw the control points.
  527.  */
  528.  - selectObject
  529.  {
  530.      float            knobsize;
  531.  
  532.     NXRect        drawRect;
  533.  
  534.     [graphicId  setSelected:YES];
  535.  
  536.     knobsize = -[self  controlPointSize];
  537.     [graphicId  getBounds:&drawRect];
  538.     NXInsetRect(&drawRect, knobsize, knobsize);
  539.  
  540.     [self  drawControl:graphicId  forRect:&drawRect  withFlags:NOFLAGS];
  541.     [window  flushWindow];
  542.  
  543.     return self;
  544. }
  545.  
  546. /*
  547. *    Redraw thecontrol points for the occupied portion. The control points
  548. *    are drawn in this view since they can be drawn quickly.
  549. */
  550. - deselectObject
  551. {
  552.     float            knobsize;
  553.  
  554.     NXRect        drawRect;
  555.  
  556.     [graphicId  setSelected:NO];
  557.  
  558.     knobsize = -[self  controlPointSize];
  559.     [graphicId  getBounds:&drawRect];
  560.     NXInsetRect(&drawRect, knobsize, knobsize);
  561.  
  562.     [self  drawControl:graphicId  forRect:&drawRect  withFlags:CLEARFLAG];
  563.     [window  flushWindow];
  564.     
  565.     return self;
  566. }
  567.  
  568. /*
  569. *    Test for a mouse down hit on either the control points or the object.
  570. */ 
  571. - testObject:(NXEvent *)event
  572. {
  573.     int            pt_num;
  574.     
  575.     NXPoint        p;
  576.  
  577.      p = event->location; 
  578.     [self convertPoint:&p fromView:nil];
  579.     [self  lockFocus];
  580.         if ([graphicId  selected])
  581.         {
  582.             if ([self  checkControl:&p :&pt_num])
  583.                 [self  redrawObject:pt_num];
  584.             else if ([self  checkObject:&p])
  585.                 [self  moveObject:event];
  586.             else
  587.                 [self  deselectObject];
  588.         }
  589.         else if ([self checkObject:&p])
  590.             [self  selectObject];
  591.     [self  unlockFocus];    
  592.  
  593.     return self;
  594. }
  595.  
  596. /*
  597.  *    Pass the file name to the factory TIFF or EPS object to create a new
  598.  *    instance. If successful then free the previous epsf object.
  599.  */
  600. - importFile:(const char *) file
  601. {
  602.     id        tempId;
  603.  
  604.     char        *end;
  605.  
  606.     NXStream    *stream;
  607.  
  608.     if (file)
  609.     {
  610.         end = strrchr(file, '.');
  611.         if (end)
  612.         {
  613.             stream = NXMapFile(file, NX_READONLY);
  614.             if (stream)
  615.             {
  616.                 if (strncmp(end, ".tiff", 5) == 0 ||
  617.                     strncmp(end, ".ps", 3) == 0 ||
  618.                         strncmp(end, ".eps", 4) == 0)
  619.                 {
  620.                      tempId = [[GraphicImport alloc]  initFromStream:stream];
  621.                     if (tempId)
  622.                     {
  623.                         [tempId  setFilename:file];
  624.                         [self  delete:self];
  625.  
  626.                         [graphicId  free];
  627.                         graphicId = tempId;
  628.                         [NXApp  setOperation:OP_IMPORT];
  629.  
  630.                         return self;
  631.                     }
  632.                     NXCloseMemory(stream, NX_FREEBUFFER);
  633.                 }
  634.                 else
  635.                     Notify("Import Error", "Unable to open file.");
  636.             }
  637.         }
  638.         else
  639.             Notify("Import Error", "Unable to import file. Unrecognized file type.");
  640.     }
  641.  
  642.     return nil;
  643. }
  644.  
  645. /*
  646.  *    Place the epsId with its upper left corner at p;
  647.  */
  648. - placeObjectAt:(const NXPoint *) p
  649. {
  650.     float            knobsize;
  651.  
  652.     NXPoint        pt;
  653.  
  654.     NXRect        rect_draw;
  655.  
  656.     if (graphicId)
  657.     {
  658.         pt = *p;
  659.  
  660.         [graphicId  getBounds:&rect_draw];
  661.         pt.y = pt.y - rect_draw.size.height;
  662.         [graphicId  setOrigin:&pt];
  663.  
  664.         knobsize = -[self  controlPointSize];
  665.         [graphicId  getBounds:&rect_draw];
  666.         NXInsetRect(&rect_draw, knobsize, knobsize);
  667.  
  668.         [self  display:&rect_draw :1];
  669.     }
  670.  
  671.     return self;
  672. }
  673.  
  674. /*
  675.  *    Begins the setup for placing an imported file into the document.
  676.  *    The object for the file is created and then waits for the mouse
  677.  *    down to begin placement and sizing. This method first gets the
  678.  *    object and then messages the redrawObject method to draw
  679.  *    the subsequent sizing rectangles.
  680.  */
  681. - importObject:(NXEvent *)event
  682. {
  683.     float            knobsize;
  684.  
  685.     NXPoint        p;
  686.  
  687.     NXRect        rect_draw;
  688.  
  689.      if (graphicId)
  690.     {
  691.         p = event->location; 
  692.         [self convertPoint:&p fromView:nil];
  693.  
  694.         NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN);
  695.         [graphicId  setBounds:&rect_draw];
  696.  
  697.         knobsize = -[self  controlPointSize];
  698.         [graphicId  getBounds:&rect_draw];
  699.         NXInsetRect(&rect_draw, knobsize, knobsize);
  700.  
  701.         [self  lockFocus];
  702.             [self  drawObject:graphicId  forRect:&rect_draw  withFlags:REDRAWFLAG];
  703.             [self  drawControl:graphicId  forRect:&rect_draw  withFlags:NOFLAGS];
  704.             [self  redrawObject:8];
  705.  
  706.             if ([graphicId  error])
  707.             {
  708.                 [graphicId  getBounds:&rect_draw];
  709.                 NXInsetRect(&rect_draw, knobsize, knobsize);
  710.                 [graphicId  free];
  711.                 graphicId = NULL;
  712.  
  713.                 [self  display:&rect_draw  :1];
  714.             }
  715.             else
  716.                 [self  selectObject];
  717.         [self  unlockFocus];
  718.     }
  719.  
  720.     return self;
  721. }
  722.  
  723.  /*
  724.  *    Depending on the current operation, check for selection, zoom or
  725.  *    import a file.
  726.  */
  727. - mouseDown:(NXEvent *)event
  728. {
  729.     int        operation;
  730.  
  731.     operation = [NXApp  operation];
  732.     switch (operation)
  733.     {
  734.         case  OP_SELECT:
  735.             [self  testObject:event];
  736.             break;
  737.         case  OP_IMPORT:
  738.             [self  importObject:event];
  739.             [NXApp  setOperation:OP_SELECT];
  740.             break;
  741.     }
  742.  
  743.     return self;
  744. }
  745.  
  746. /*
  747.  *    Deletes the epsf object if selected.
  748.  */
  749. - keyDown:(NXEvent *) event
  750. {
  751.     if ([NXApp  operation] == OP_SELECT && 
  752.          event->data.key.charSet == NX_ASCIISET &&
  753.          event->data.key.charCode == NX_DELETE)
  754.         return [self  delete:self];
  755.     else
  756.         return nil;
  757. }
  758.  
  759. /*
  760. *    Draw the control points using the user path buffer to hold
  761. *    the data for the xyshow.
  762. */
  763. - drawControl:object  forRect:(NXRect *)r  withFlags:(int)flags
  764. {
  765.     float            knobsize;
  766.  
  767.     NXPoint        lastpoint;
  768.     
  769.     NXRect        rect;
  770.  
  771.     UPath        *upathBuffer;
  772.  
  773.     if (r)
  774.         rect = *r;
  775.     else
  776.         [self  getVisibleRect:&rect];
  777.  
  778.     if (flags & CLEARFLAG)
  779.         compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY);
  780.  
  781.     if ([object  selected])
  782.     {
  783.         lastpoint.x = 0;
  784.         lastpoint.y = 0;
  785.  
  786.         upathBuffer = [NXApp upathBuffer];
  787.         upathBuffer->num_ops = 0;
  788.         upathBuffer->num_pts = 0;
  789.  
  790.         knobsize = [self  controlPointSize];
  791.         NXInsetRect(&rect, -knobsize/2, -knobsize/2);
  792.         [object  putControlPoints:upathBuffer  forRect:&rect  :&lastpoint];
  793.  
  794.         upathBuffer->ops[upathBuffer->num_ops] = 0;
  795.         upathBuffer->pts[upathBuffer->num_pts] = 0;
  796.         upathBuffer->pts[upathBuffer->num_pts + 1] = 0;
  797.  
  798.         if (upathBuffer->num_ops > 0)
  799.         {
  800.             PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15); 
  801.             PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1],
  802.                 &upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops);
  803.         }
  804.     }
  805.  
  806.     return self;
  807. }
  808.  
  809. - drawObject:object  forRect:(NXRect *)r  withFlags:(int) flags
  810. {
  811.     NXRect        rect;
  812.  
  813.     if (r)
  814.         rect = *r;
  815.     else
  816.         [self  getVisibleRect:&rect];
  817.  
  818.     [object  drawObject:&rect  withFlags:flags  inView:self];
  819.  
  820.     return self;
  821. }
  822.  
  823. /*
  824. *    Fill in the background of the rectangle and then draw the epsf object.
  825. *    If NX_DRAWING, then draw into the buffer and composite it into
  826. *    the window.
  827. */
  828. - drawSelf:(NXRect *)r :(int) count
  829. {
  830.     if (NXDrawingStatus == NX_DRAWING)
  831.     {
  832.         [bufferId  lockFocus];
  833.         PSsetgray(NX_WHITE);
  834.         NXRectFill(r);
  835.     }
  836.  
  837.     [self  drawObject:graphicId  forRect:r  withFlags:REFRESHFLAG];
  838.  
  839.     if (NXDrawingStatus == NX_DRAWING && !imageId)
  840.     {
  841.         [bufferId  unlockFocus];
  842.         compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
  843.         [self  drawControl:graphicId  forRect:r  withFlags:NOFLAGS];
  844.     }
  845.     
  846.     return self;            
  847. }
  848.  
  849. /*
  850. *    This method is only overridden to eliminate during a copy
  851. *    the rectclip and gsave/grestore pairing that results from
  852. *    a lockFocus. These are usually harmless operations but
  853. *    they interfere with trying to produce Illustrator format files.
  854. */
  855. - display:(NXRect *)r  :(int) count  :(BOOL)flag
  856. {
  857.     if (NXDrawingStatus == NX_COPYING)
  858.     {
  859.         [self  drawSelf:r  :count];
  860.         DPSFlushContext(DPSGetCurrentContext());
  861.     }
  862.     else
  863.         [super  display:r  :count  :flag];
  864.  
  865.     return self;
  866. }
  867.  
  868. - (BOOL)acceptsFirstResponder
  869. {
  870.     return YES;
  871. }
  872.  
  873. /*
  874. *    Used when printing. Returns the global resources used in the document.
  875. */
  876. - addResources:(Resource *) resourceDoc
  877. {
  878.     NXAtom        string;
  879.  
  880.     if (NXDrawingStatus == NX_COPYING)
  881.     {
  882.         string = NXUniqueString(EpsfProcSet);
  883.         if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT])
  884.             resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List  new];
  885.  
  886.         [resourceDoc[RES_PROCSETS].states[RES_PRESENT]
  887.                         addObjectIfAbsent:(id) string]; 
  888.  
  889.         if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED])
  890.             resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List  new];
  891.  
  892.         [resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]
  893.                         addObjectIfAbsent:(id) string]; 
  894.     }    
  895.  
  896.     return self;
  897. }    
  898.  
  899. /*
  900. *    Print the %%DocumentResource comments. A list of the resources is
  901. *    accumulated from the epsf files (only one in this case).
  902. *    The list is then written to the current context.
  903. */
  904. - beginResourceComments:(const NXRect *) bbox
  905. {
  906.     int            i, j;
  907.  
  908.     Resource        resourceDoc[RES_NUMTYPES];
  909.  
  910.     bzero(&resourceDoc, sizeof(resourceDoc));
  911.     [self  addResources:resourceDoc];
  912.     [graphicId  addResources:resourceDoc  for:(NXRect *) bbox];
  913.  
  914.     for (i = 0; i < RES_NUMTYPES; i++)
  915.     {
  916.         for (j = 0; j < RES_NUMSTATES; j++)
  917.         {
  918.             if (resourceDoc[i].states[j])
  919.             {
  920.                 WriteEpsfResource(resourceDoc[i].states[j], i, j);
  921.                 [resourceDoc[i].states[j]  free];
  922.             }
  923.         }
  924.     }
  925.  
  926.     return self;
  927. }
  928.  
  929. /*
  930. *    Write out the necessary information. Overridden to include
  931. *    the fonts from the EPSF files.
  932. */
  933. - beginPrologueBBox:(const NXRect *)boundingBox 
  934.     creationDate:(const char *)dateCreated 
  935.     createdBy:(const char *)anApplication 
  936.     fonts:(const char *)fontNames 
  937.     forWhom:(const char *)user 
  938.     pages:(int)numPages 
  939.     title:(const char *)aTitle
  940. {
  941.     time_t        clock;
  942.  
  943.     DPSContext    ctxt;
  944.  
  945.     ctxt = DPSGetCurrentContext();
  946.     if (!boundingBox)
  947.         boundingBox = &bounds;
  948.  
  949.     if (!dateCreated)
  950.     {
  951.         clock = time(0);
  952.         dateCreated = ctime(&clock);
  953.     }
  954.  
  955.     if (!anApplication)
  956.         anApplication = [NXApp  appName];
  957.  
  958.     if (!user)
  959.         user = (char *) getlogin();
  960.  
  961.     if (numPages <= 0)
  962.         numPages = 1;
  963.  
  964.     if (!aTitle)
  965.         aTitle = [[window  delegate]  filename];
  966.  
  967.     DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n");
  968.     DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication);
  969.     DPSPrintf(ctxt, "%%%%For: %s\n", user);
  970.     DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle);
  971.     DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated);
  972.     DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x),
  973.         (int) floor(boundingBox->origin.y),
  974.         (int) ceil(boundingBox->origin.x + boundingBox->size.width),
  975.         (int) ceil(boundingBox->origin.y + boundingBox->size.height));
  976.     if (NXDrawingStatus == NX_COPYING)
  977.         DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396);
  978.     if (NXDrawingStatus != NX_COPYING)
  979.         DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages);
  980.  
  981.     [self  beginResourceComments:boundingBox];
  982.  
  983.     return self;
  984. }
  985.  
  986. /*
  987. *    Includes the abbreviated Illustrator proc set so that
  988. *    the eps files produced through Save To will print on their
  989. *    own. Also includes the preview data as a comment when
  990. *    specified.
  991. */
  992. - endHeaderComments
  993. {
  994.     DPSContext    ctxt;
  995.  
  996.     if (NXDrawingStatus == NX_COPYING)
  997.     {
  998.         ctxt = DPSGetCurrentContext();
  999.         DPSPrintf(ctxt, "%%%%EndComments\n\n");
  1000.  
  1001.         DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n");
  1002.         WriteEpsfProcSetDef ();
  1003.         DPSPrintf(ctxt, "%%%%EndProcSet\n\n");
  1004.  
  1005.         if (imageId && [[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
  1006.             WriteEpsfPreview(imageId);
  1007.     }
  1008.     else
  1009.         [super  endHeaderComments];
  1010.  
  1011.     return self;
  1012. }
  1013.  
  1014. /*
  1015. *    If saving in illustrator, override the prologue comment.
  1016. */
  1017. - endPrologue
  1018. {
  1019.     DPSContext    ctxt;
  1020.  
  1021.     if (NXDrawingStatus == NX_COPYING)
  1022.     {
  1023.         ctxt = DPSGetCurrentContext();
  1024.         DPSPrintf(ctxt, "%%%%EndProlog\n\n");
  1025.     }
  1026.     else
  1027.         [super  endPrologue];
  1028.  
  1029.     return self;
  1030. }
  1031.  
  1032. /*  Initialize the Illustrator abbreviated proc set. */
  1033. - beginSetup
  1034. {
  1035.     DPSContext    ctxt;
  1036.  
  1037.     if (NXDrawingStatus == NX_COPYING)
  1038.     {
  1039.         ctxt = DPSGetCurrentContext();
  1040.         DPSPrintf(ctxt, "%%%%BeginSetup\n");
  1041.         WriteEpsfProcSetInit();
  1042.     }
  1043.     else
  1044.         [super  beginSetup];
  1045.         
  1046.     return self;
  1047. }
  1048.  
  1049. - endSetup
  1050. {
  1051.     DPSContext    ctxt;
  1052.  
  1053.     if (NXDrawingStatus == NX_COPYING)
  1054.     {
  1055.         ctxt = DPSGetCurrentContext();
  1056.         DPSPrintf(ctxt, "%%%%EndSetup\n");
  1057.     }
  1058.     else
  1059.         [super  endSetup];
  1060.         
  1061.     return self;
  1062. }
  1063.  
  1064. /*  Terminate the Illustrator abbreviated proc set. */
  1065. - beginTrailer
  1066. {
  1067.     DPSContext    ctxt;
  1068.  
  1069.     if (NXDrawingStatus == NX_COPYING)
  1070.     {
  1071.         ctxt = DPSGetCurrentContext();
  1072.         DPSPrintf(ctxt, "%%%%Trailer\n");
  1073.         WriteEpsfProcSetTerm();
  1074.     }
  1075.     else
  1076.         [super  beginTrailer];
  1077.  
  1078.     return self;
  1079. }
  1080.  
  1081. - endTrailer
  1082. {
  1083.     if (NXDrawingStatus != NX_COPYING)
  1084.         [super  endTrailer];
  1085.  
  1086.     return self;
  1087. }
  1088.  
  1089. @end
  1090.